using System;
using System.IO;
using System.Text;
using System.Threading;
using ICSharpCode.SharpZipLib;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Checksums;

namespace BLF.WWW_WWW.Wiki
{
	/// <summary>
	/// Allows to create a Zip File using #ZipLib.
	/// </summary>
	public static class ZipFile {

		/// <summary>
		/// Creates a Zip File.
		/// </summary>
		/// <param name="files">The Files to include in the Zip File.</param>
		/// <param name="baseDir">The Base directory of the Files (it will be removed from their full path).</param>
		/// <param name="destinationStream">The destination Stream.</param>
		/// <param name="level">The compression level (0 min - 9 max).</param>
		public static void Create(string[] files, string baseDir, Stream destinationStream, int level) {
			ZipOutputStream output = new ZipOutputStream(destinationStream);
			output.SetLevel(level);

			if(!baseDir.EndsWith("\\")) baseDir += "\\";
			Crc32 crc32 = new Crc32();

			string errors = "";
			int errCount = 0;

			ZipEntry entry = null;
			FileStream input = null;
			for(int i = 0; i < files.Length; i++) {
				input = null;
				files[i] = files[i].Substring(baseDir.Length);
				entry = new ZipEntry(files[i]);
				for(int k = 0; k < Settings.FileAccessTries; k++) {
					try {
						input = new FileStream(baseDir + files[i], FileMode.Open, FileAccess.Read, FileShare.Read);
						break;
					}
					catch {
						input = null;
						Thread.Sleep(Settings.FileAccessTryDelay);
					}
				}
				if(input == null) {
					//throw new IOException("Unable to open file: " + baseDir + files[i]);
					errors += "Unable to open file: " + baseDir + files[i] + "\r\n";
					errCount++;
					continue;
				}

				int read = 0;
				byte[] buff = new byte[65536];
				crc32.Reset();

				output.PutNextEntry(entry);

				do {
					read = input.Read(buff, 0, buff.Length);
					output.Write(buff, 0, read);
					crc32.Update(buff, 0, read);
				} while(read > 0);

				input.Close();

				entry.Crc = crc32.Value;
				FileInfo fInfo = new FileInfo(baseDir + files[i]);
				entry.DateTime = fInfo.LastWriteTime;
				entry.Size = fInfo.Length;

			}

			if(errCount == 0) errors = "Backup completed successfully at " + DateTime.Now.ToString("R") + ".";
			else errors += "Backup completed with " + errCount.ToString() + " errors at " + DateTime.Now.ToString("R") + ".";
			byte[] e = Encoding.UTF8.GetBytes(errors);
			entry = new ZipEntry("ZipBackupReport.txt");
			crc32.Reset();
			output.PutNextEntry(entry);
			output.Write(e, 0, e.Length);
			crc32.Update(e);
			entry.Crc = crc32.Value;
			entry.DateTime = DateTime.Now;
			entry.Size = e.Length;

			output.Finish();
			output.Close();
		}

		/// <summary>
		/// Creates a Zip File.
		/// </summary>
		/// <param name="files">The Files to include in the Zip File.</param>
		/// <param name="baseDir">The Base directory of the Files (it will be removed from their full path).</param>
		/// <param name="destination">The destination Zip File.</param>
		/// <param name="level">The compression level (0 min - 9 max).</param>
		public static void Create(string[] files, string baseDir, string destination, int level) {
			Create(files, baseDir, File.Create(destination), level);
		}

	}

}
